#ifndef BACKPACK_TERMINALVIEW_H_
#define BACKPACK_TERMINALVIEW_H_

#include "View.hpp"
#include "ItemVariant.hpp"
#include "UserActivity.hpp"
#include "Action.hpp"

#include <iostream>
#include <string>
#include <map>
#include <list>
#include <algorithm>

#include <boost/mpl/for_each.hpp>
#include <boost/mpl/range_c.hpp>


namespace backpack {




class TerminalView {
public:
  TerminalView(std::istream &in, std::ostream &out)
    : m_rInStream(in), m_rOutStream(out) {
  }

  NodeDescriptionBuilder<TerminalView> newNodeDescription();
  UserChoiceBuilder<TerminalView> newUserChoice();
  void showMessage(std::string const& message);
  void showNewItem(ItemVariant const& item);

  TerminalView& clearScreen() {
    typedef boost::mpl::range_c<int, 0, 128> int0to128;
    boost::mpl::for_each<int0to128>([=] (int) { printLine(); });
    return *this;
  }
  TerminalView& printLine(std::string const& line = "") {
    m_rOutStream << line << std::endl;
    return *this;
  }

private:
  std::istream &m_rInStream;
  std::ostream &m_rOutStream;

  template <class Type>
  TerminalView& print(Type const& printable) {
    m_rOutStream << printable;
    return *this;
  } 
  TerminalView& clearInputBuffer() {
    // platform dependent :/
    return *this;
  }

  char readSingleChar() {
    char c;
    m_rInStream.get(c);
    readLine(); // read the rest
    return c;
  }
  std::string readLine() {
    std::string line;
    std::getline(m_rInStream, line);
    return line;
  }

  friend class NodeDescriptionBuilder<TerminalView>;
  friend class UserChoiceBuilder<TerminalView>;
}; // class TerminalView




template <>
class NodeDescriptionBuilder<TerminalView> {
public:
  NodeDescriptionBuilder(TerminalView &view)
    : m_rView(view), m_strNodeDesc("") {
  }

	void setNodeDescription(std::string && description) {
    m_strNodeDesc = std::forward<std::string>(description);
  }
	void setNodeDescription(std::string const& description) {
    m_strNodeDesc = description;
  }
	void addWayPointDescription(std::string &&description) {
    addHotSpotDescription(std::forward<std::string>(description));
  }
	void addTouchPointDescription(std::string &&description) {
    addHotSpotDescription(std::forward<std::string>(description));
  }
	void addUnlockPointDescription(std::string &&description) {
    addHotSpotDescription(std::forward<std::string>(description));
  }
	void waitForPlayerConfirmation() const;

private:
  TerminalView &m_rView;
  std::string m_strNodeDesc;
  std::list<std::string> m_listHotSpotDesc;

  void addHotSpotDescription(std::string &&description) {
    m_listHotSpotDesc.push_back(std::forward<std::string>(description));
  }
}; // class NodeDescriptionBuilder<TerminalView>




template <>
class UserChoiceBuilder<TerminalView> {
public:
  UserChoiceBuilder(TerminalView &view)
    : m_rView(view), m_strTitle(""), m_cNextOptionId('a') {
  }

  void setTitle(std::string && title) {
    m_strTitle = std::forward<std::string>(title);
  }
  void setTitle(std::string const& title) {
    m_strTitle = title;
  }
  void addOption(std::string &&desc, UserActivityVariant &&activity) {
    addOption(m_cNextOptionId++,
        std::forward<std::string>(desc),
        std::forward<UserActivityVariant>(activity));
  }
  void addOption(char key, std::string &&desc, UserActivityVariant &&activity) {
    m_mapOptions.insert(std::make_pair(key,
          OptionTuple(
            std::forward<std::string>(desc),
            std::forward<UserActivityVariant>(activity)
            )));
  }
  UserActivityVariant const& letPlayerChoose();

private:
  struct OptionTuple {
    std::string description;
    UserActivityVariant activity;
    OptionTuple(std::string && d, UserActivityVariant && a)
      : description(std::forward<std::string>(d)),
      activity(std::forward<UserActivityVariant>(a)) {
    }
  };

  TerminalView &m_rView;
  std::string m_strTitle;
  char m_cNextOptionId;
  std::map<char, OptionTuple> m_mapOptions;
}; // class UserChoiceBuilder<TerminalView>




} // namespace backpack


#endif // BACKPACK_TERMINALVIEW_H_

